/* ==================================================================   
 * Created [2009-4-27 下午11:32:55] by Jon.King 
 * ==================================================================  
 * TSS 
 * ================================================================== 
 * mailTo:jinpujun@hotmail.com
 * Copyright (c) Jon.King, 2009-2012 
 * ================================================================== 
*/

package com.jinhe.tss.core.cachepool;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

import com.jinhe.tss.core.cachepool.container.IPoolContainer;
import com.jinhe.tss.core.cachepool.strategy.CacheStrategy;
import com.jinhe.tss.core.exception.BusinessException;


/** 
 * <p> AbstractPool.java </p> 
 * 
 * 对象池抽象类，定义通用的方法。
 * 
 * @author Jon.King 2007-1-3
 *
 */
public abstract class AbstractPool implements IPool {
    protected Logger log = Logger.getLogger(this.getClass());

    // 对象池属性
    protected long requests; // 请求数
    protected long hits;    // 命中数
    protected boolean released = false;      // 是否已释放
    protected boolean asyncDestroy = false; // 是否异步销毁
    
    protected ICacheLoader loader;        // 缓存项载入类
    protected IArithmetic  arithmetic;   // 缓存算法类
    protected CacheStrategy strategy;   // 缓存策略

    protected Set<IPoolListener> listeners = new HashSet<IPoolListener> (); // 监听器列表

    public abstract void init();

    public abstract int getSize();

    public abstract void release(boolean forced) ;

    public abstract IPoolContainer getFree();

    public abstract IPoolContainer getUsing();
 
    public Cacheable getObject(Object key) {
        if(key == null) { 
            return null;
        }
        
        Cacheable item = getOldObject(key);
        addRequests();
        if(item != null){
            addHits();
            item.addHit();
            item.updateAccessed();
        } 
        else {
            //调用ICacheLoader来载入需要的缓存项，取到则放入缓存池中
            item = reload(new TimeWrapper(key, null));
            if(item != null) {
                putObject(item.getKey(), item.getValue());
            }
        }       
        return item;
    }
    
    public Cacheable getOldObject(Object key) {
        if (released) {
            log.error("缓存池(" + this.getName() + ")已经被释放!");
        }
        return getFree().get(key) == null ? getUsing().get(key) : getFree().get(key); 
    }

    public Cacheable putObject(Object key, Object value) {
        if(key == null) {
            return null;
        }
             
        Cacheable oldItem = getOldObject(key);
        if(oldItem != null){
            oldItem.update(value);
            return oldItem;
        }else{
            //缓存项放入缓存池的同时也设置了其生命周期
            Cacheable newItem = getFree().put(key, new TimeWrapper(key, value, strategy.getCyclelife()));
            firePoolEvent(ObjectPoolEvent.PUT_IN);//事件监听器将唤醒所有等待中的线程，包括cleaner线程，checkout，remove等操作的等待线程
            return newItem;
        }
    }

    public Cacheable removeObject(Object key) {
        Cacheable item = getFree().remove(key);
        firePoolEvent(ObjectPoolEvent.REMOVE);
        return item;
    }

    public List<Cacheable> listItems() {
        Set<Cacheable> values = new HashSet<Cacheable>();
        if(getFree() != null) {
            values.addAll(getFree().getValues());
        }
        if(getUsing() != null) {
            values.addAll(getUsing().getValues());
        }
        
        return new ArrayList<Cacheable>(values);
    }
    
    public List<CacheableKey> listKeys() {
        Set<CacheableKey> keys = new HashSet<CacheableKey>();
        if(getFree() != null) {
            keys.addAll(getFree().getKeys());
        }
        if(getUsing() != null) {
            keys.addAll(getUsing().getKeys());
        }
        
        return new ArrayList<CacheableKey>(keys);
    }
    
    public void flush() {
        release(true);
        resetHitCounter();
        log.debug("已经清除池中所有的缓存项。");
    }
    
    public Cacheable reload(final Cacheable obj) throws RuntimeException {
        Cacheable newObj = loader.reloadCacheObject(obj);
        
        //如果重新加载的缓存项为空，则将原先的缓存项从缓存池中移除，否则则覆盖原有的缓存项。
        if(newObj == null)
            removeObject(obj.getKey());
        else
            newObj = putObject(newObj.getKey(), newObj.getValue());
        return newObj;
    }
    
    /**
     * 销毁指定对象（如果有必要的话可采用异步）；
     */
    public void destroyObject(final Cacheable o) {
        if (o == null)
            return;
        if (isAsyncDestroy()){
            Thread t = new Thread(new Runnable(){
                public void run() { 
                    arithmetic.destroy(o); 
                }
            });
            t.start();
        }else
            arithmetic.destroy(o);

    }
    
    public final void releaseAsync(final boolean forced) {
        Thread t = new Thread(new Runnable(){
            public void run() { 
                release(forced); 
            }
        });
        t.start();
    }
    
    public CacheStrategy getCacheStrategy() { return this.strategy; }
    
    public void setCacheStrategy(CacheStrategy strategy) { 
        //判断是否是修改缓存策略还是在初始化缓存池，初始化的话strategy为null
        if(this.getCacheStrategy() != null)
            this.strategy.fireEventIfChanged(strategy);//缓存策略改变则触发事件
        else
            this.strategy = strategy; 
    }
    
    public void setArithmetic(IArithmetic arithmetic) { this.arithmetic = arithmetic; }

    public void setLoader(ICacheLoader loader) { this.loader = loader; }
    
    public IArithmetic getArithmetic(){ return this.arithmetic; }

    public String getName() { return getCacheStrategy().getName(); }

    public long getRequests() {
        return requests;
    }
    
    protected void addRequests() {  requests++; }

    protected void addHits() { hits++; }
    
    /**
     * 重置请求数和点击数
     * requests = hits = 0
     */
    protected final void resetHitCounter() { requests = hits = 0; }  
    
    public final float getHitRate() {
        return (requests == 0) ? 0 : (((float) hits / requests) * 100f);
    }
    
    public final void setAsyncDestroy(boolean b) { asyncDestroy = b; }

    public final boolean isAsyncDestroy() { return asyncDestroy; }
    
    public final boolean isReleased() { return this.released; }
    
    public final void firePoolEvent(int eventType) {
        if (listeners.isEmpty()) return;
        
        ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, eventType);
        for (Iterator<IPoolListener> iter = listeners.iterator(); iter.hasNext();){
            (iter.next()).dealwithPoolEvent(poolEvent);
        }
    }
    
    public final void addObjectPoolListener(IPoolListener x){
        listeners.add(x);
    }

    public final void removeObjectPoolListener(IPoolListener x){
        listeners.remove(x);
    }
    
    protected Cacheable checkOut() {
        Cacheable item = getFree().getByAccessMethod(getCacheStrategy().getAccessMethod());
        if(item != null){
            item.addHit();
            getFree().remove(item.getKey());
            getUsing().put(item.getKey(), item);
        }
        return item;
    }
    
    public Cacheable checkOut(long timeout) {
        if(timeout <= 0) {
            timeout = this.getCacheStrategy().getInterruptTime();
        }
        
        long time = System.currentTimeMillis();
        Cacheable o = null;
        o = checkOut();
        synchronized(this){
            while (o == null  &&  (System.currentTimeMillis() - time < timeout)) {
                try {
                    log.debug("缓存池(" + this.getName() + ")中没有可用的缓存项......等待 " + timeout + "（毫秒）");
                    wait(timeout);
                    o = checkOut();
                }catch (InterruptedException e) { 
                    log.error("检出时等待被中断", e); 
                }
            }
        }
        if(o == null){
            String errorMsg = "缓存池(" + this.getName() + ")已满，且各缓存项都处于使用状态，需要等待。可考虑重新设置缓存策略！";
            log.error(errorMsg);
            throw new BusinessException(errorMsg);
        }
        return o;
    }

    public void checkIn(Cacheable o) {
        if (o == null){
            log.error("试图返回空的缓存项");
            return;
        }
        //判断对象是否存在using池中，是的话将对象从using池中移出，否则抛出异常
        if(!o.equals(getUsing().remove(o.getKey()))){
            log.error("试图返回不是using池中对象到free中，返回失败！ " + getName());
            throw new BusinessException("试图返回不是using池中对象到free中，返回失败！ " + getName());
        }
        Object value = o.getValue();
        //如果池已满，则销毁对象，否则则放回池中
        int maxSize = getCacheStrategy().getPoolSize().intValue();
        if (maxSize > 0 && getSize() >= maxSize){
            destroyObject(o);
        }else{
            try{ 
                // 如果对象实现了Reusable接口，则执行重置操作
                if(value instanceof Reusable) {
                    ((Reusable)value).recycle(); 
                }
                getFree().put(o.getKey(), o); //重新放入free池中,其点击率等属性已改变
                firePoolEvent(ObjectPoolEvent.CHECKIN);//事件监听器将唤醒所有等待中的线程，包括cleaner线程，checkout，remove等操作的等待线程
                log.debug(o.getValue() + " 缓存项已经回收！");
            }catch (Exception e){                   
                destroyObject(o); // 如果不能回收则销毁
                log.error("无法回收缓存项，已销毁！",  e);
            }
        }
    }   
    
    public Cacheable remove() {
        Cacheable o = getFree().getByAccessMethod(getCacheStrategy().getAccessMethod().intValue());
        
        //如果free池中取不到，则要等using池中的缓存对象返回到free中。线程等待
        long timeout = getCacheStrategy().getInterruptTime().longValue();
        long time = System.currentTimeMillis();
        synchronized(this){
            while (o == null  &&  (System.currentTimeMillis() - time < timeout)) {
                try {
                    log.debug("free缓存池(" + this.getName() + ")中没有可用的项......等待 " + timeout + "ms");
                    wait(timeout);
                    o = checkOut();
                }catch (InterruptedException e) { 
                    log.error("等待移除 checkIn 对象时等待被中断", e); 
                }
            }
        }
        removeObject(o.getKey());        
        return o;
    }
    
    public boolean purge() {
        log.debug("开始清除 (\"" + this.getName() + "\") 池中过期的缓存项 ....... ");
        int count = 0;
        Cacheable item = null;
        for (CacheableKey key : getFree().getKeys()){
            item = getFree().get(key);
            if (item != null && item.isExpired()){
                removeObject(item.getKey());
                destroyObject(item);
                count++;
            }
        }
        log.debug("共清除了 (\"" + this.getName() + "\") 池中 " + count + " 个缓存对象。");
        return getFree().size() > 0  ||  count > 0;
    }
}

